LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;

entity control is
  port ( 
    op: IN STD_LOGIC_VECTOR (5 downto 0); --Upper 6 bits of MIPS instruction
    funct : IN std_logic_vector (5 downto 0);
    RegDst: OUT STD_LOGIC;    --mux control
    Jump: OUT STD_LOGIC;
    Branch: OUT STD_LOGIC;    
    BranchNE: OUT STD_LOGIC;
    MemRead: OUT STD_LOGIC;   --ramd read enable
    MemtoReg: OUT STD_LOGIC;  --mux control
    ALUOp: OUT STD_LOGIC_VECTOR (3 downto 0);
    MemWrite: OUT STD_LOGIC;  --ramd write enable
    ALUSrc: OUT STD_LOGIC;    --mux control
    RegWrite: OUT STD_LOGIC; --register write enable
    ExtOp : OUT STD_LOGIC;
    Shift : OUT STD_LOGIC;
    LoadUpper : OUT STD_lOGIC;
    JumpReg   : OUT STD_LOGIC;
    Link      : OUT STD_LOGIC
  );
end control;

architecture behav of control is

  signal opcode, fun : std_logic_vector(7 downto 0);
  signal rformat_opcode : std_logic_vector (3 downto 0);
  signal ALUSrc_i, Shift_i : std_logic;
begin
  opcode <= "00" & op;
  fun    <= "00" & funct;
--R-format op = x"0"
-- addu, and, jr, nor, or, slt, sltu, sll, srl, subu, xor
-- these instructions use the funct field of the instruction to decode the ALU_op

--I-format op
-- addiu  =  09
-- andi   =  0C
-- beq    =  04
-- bne    =  05
-- lui    =  0F
-- lw     =  23
-- ori    =  0D
-- slti   =  0A
-- sltiu  =  0B
-- sw     =  2B
-- xori   =  0E

--J-format
-- J = 2
-- JAL = 3

--HALT = xFF

  Link <= '1' when opcode = x"30" or opcode = x"38" else '0';

  LoadUpper <= '1' when opcode = x"0F" else
               '0';

  JumpReg <= '1' when (opcode = x"00" and fun = x"08") else --(not (op(5) and op(4) and op(3) and op(2) and op(1) and op(0))) and (not funct(4) and funct(3) and not funct(2) and not funct(1) and not funct(0));
             '0';
  
  with opcode select
    RegDst <= '1' when x"00",
              '0' when others;
              
  with opcode select
    Jump <=   '1' when x"02" | x"03", --J | JAL
              '0' when others;

  with opcode select
    Branch <= '1' when x"04", --BEQ 
              '0' when others;
              
  BranchNE <= '1' when opcode = x"05" else --BNE
              '0';

--Useless anyway              
  with opcode select
    MemRead <= '0' when x"00" | x"2B",   
               '1' when x"23" | x"30",   --LW, LL
               '0' when others;
              
  with opcode select
    MemWrite <= '0' when x"00" | x"23" | x"0F", --R-format, LW, LUI
                '1' when x"2B" | x"38", --SW  SC
                '0' when others;
                
  with opcode select
    MemtoReg <= '0' when x"00",
                '1' when x"23" | x"30" | x"38" | x"0F", --LW, LL, SC, LUI--SC goes here to get the success code
                '0' when others;
              


  with opcode select
    ALUSrc_i <= '0' when x"00" ,
              --LW, LL, SW, SC, ADDIU, ORI, XORI, ANDI, SLTI, SLTIU, LUI
              '1' when x"23" | x"30" | x"2B" | x"38" | x"09" | x"0D" | x"0E" | x"0C" | x"0A" | x"0B" | x"0F", 
                            '0' when others;
  ALUSrc <= '1' when (fun = x"00" or fun = x"02") and (opcode = x"00") else
            ALUSrc_i;
              
  with opcode select            --JAL                   --SC goes here to get the success code
    RegWrite <= '1' when x"00" | x"03" | x"23" | x"30" | x"38" | x"0D" | x"0C" | x"09" | x"0F" | x"0A" | x"0B" | x"0E", --r-format, LW, ORI, ANDI
                '0' when x"2B" | x"04" | x"05",
                '0' when others;
  with opcode select
    ExtOp <=  '1' when x"23" | x"30" | x"38" | x"2B" | x"09" | x"0A" | x"0B", --lw, sw, addiu, SLTI, SLTIU
              '0' when x"00" | x"0D" | x"04" | x"02" | x"0C" | x"05" | x"0F" | x"0E", --R-type, ori, beq, jump
              '0' when others;
                 
  Shift <= '1' when opcode = x"00" and (fun = x"00" or fun = x"02") else
           '0';

  with fun select
    rformat_opcode <= "0000" when x"00", --SLL
                      "0001" when x"02", --SRL
                      "0010" when x"21", --ADDU
                      "0011" when x"23", --SUBU
                      "0100" when x"24", --AND
                      "0101" when x"27", --NOR
                      "0110" when x"25", --OR
                      "0111" when x"26", --XOR
                      "1000" when x"2A",   --SLT
                      "1001" when x"2B",   --SLTU
                      "0000" when others;
                      
--use internal signal, or simply don't specify on opcode other than 0, no the I formats need it too
  with opcode select
     ALUOp <= rformat_opcode when x"00",
              "0011" when x"04" | x"05", --BEQ | BNE
              "0100" when x"0C", --ANDI
              "0110" when x"0D", --ORI
              "0010" when x"09", --ADDIU
              "0111" when x"0E", --XORI
              "0010" when x"2B" | x"23" | x"30" | x"38", --SW, LW
              "1000" when x"0A",   --SLTI
              "1001" when x"0B",  --SLTIU
              "0000" when others;


end behav;